using System;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using System.Globalization;

using System.Threading;

using ca.metaobjects.csogateway;
using ca.metaobjects.csogateway.Configuration;

namespace ca.metaobjects.csogateway.Reflection
{
	/// <summary>
	/// Static class implementing the functionality of the Loader and the Type Morpher
	/// </summary>

	internal static class Loader
	{
		#region Static fields

		private static readonly CsoTypeCollection allCsoTypes = null;

		private static Library csoNativeTypesLibrary = null;

		private static Assembly csoGatewayAssembly = null;
		private static string[] resourceNames = null;

		#endregion

		#region Static constructor
		static Loader()
		{
			allCsoTypes = new CsoTypeCollection();

			loadResourceNames();
			loadNativeTypes();
		}
		#endregion

		#region Re-initialize loader
		internal static void reset()
		{
			allCsoTypes.Clear();

			loadResourceNames();
			loadNativeTypes();
		}
		#endregion

		#region Loading assembly ressources
		private static void loadResourceNames()
		{
			csoGatewayAssembly = Assembly.GetExecutingAssembly();
			resourceNames = csoGatewayAssembly.GetManifestResourceNames();
		}
		#endregion

		#region Loading CsoNativeTypes
		private static void loadNativeTypes()
		{
			XmlSerializer ser;
			try
			{
				ser = new XmlSerializer(typeof(Library));
			}
			catch (Exception ex)
			{
				throw new CsoGatewayError(ErrorMessage.NTL_000, ex);
			}

			csoNativeTypesLibrary = ser.Deserialize(getResourceStream("CsoNativeTypes.xml")) as Library;
			csoNativeTypesLibrary.initializeExtendedFields();

			CsoNativeType nativeType;

			foreach (TypeInformation info in csoNativeTypesLibrary.NativeTypes)
			{
				nativeType = new CsoNativeType();
				nativeType.NamespaceName = info.jsNamespace;
				nativeType.TypeName = info.typeName;
				nativeType.ScriptFileInfo = getScriptFileInfo(info, nativeType);
				nativeType.ServerSideTypes = getListOfEquivalentTypes(info);
				nativeType.ServerSideTypesToConverterMapping = getConverterMapping(nativeType);
				createStrongDependencies(info, nativeType);
				createWeakDependencies(info, nativeType);

				nativeType.markAsComplete();

				// Add newly created CsoNativeType to the CsoTypeCollection
				allCsoTypes.addCsoNativeType(nativeType);
			}
		}

		private static LibraryScriptFileInformation getScriptFileInfo(TypeInformation info, CsoNativeType nativeType)
		{
			LibraryScriptFileInformation fileInfo = allCsoTypes.getScriptFileInfo(info.inFile);

			if (fileInfo == null)
			{
				fileInfo = new LibraryScriptFileInformation(info.inFile);
				allCsoTypes.addScriptFileInfo(fileInfo);
			}
			fileInfo.addContainedType(nativeType);

			return fileInfo;
		}

		private static List<Type> getListOfEquivalentTypes(TypeInformation info)
		{
			List<Type> equivTypes;

			// Set the types that are equivalent to this CsoNativeType
			if (info.ServerSideEquivalent != null)
			{
				equivTypes = new List<Type>(info.ServerSideEquivalent.Length);

				for (int i = 0; i < info.ServerSideEquivalent.Length; ++i)
				{
					equivTypes.Add(Type.GetType(info.ServerSideEquivalent[i].type, true));
				}
			}
			else
			{
				equivTypes = new List<Type>(0);
			}

			return equivTypes;
		}

		private static Dictionary<Type, Type> getConverterMapping(CsoNativeType nativeType)
		{
			Dictionary<Type, Type> mapping = new Dictionary<Type, Type>();

			for (int i = 0; i < nativeType.ServerSideTypes.Count; ++i)
			{
				mapping.Add(nativeType.ServerSideTypes[i], csoNativeTypesLibrary.getConverter(nativeType.ServerSideTypes[i]));
			}

			return mapping;
		}

		private static void createStrongDependencies(TypeInformation info, CsoNativeType nativeType)
		{
			TypeInformation typeInfo;
			CsoType dependencyType;

			if (info.StrongDependency != null)
			{
				foreach (DependencyInformation dependencyInfo in info.StrongDependency)
				{
					typeInfo = csoNativeTypesLibrary.getCsoNativeTypeInformation(dependencyInfo.id);

					if (allCsoTypes[String.Format("{0}.{1}", typeInfo.jsNamespace, typeInfo.typeName)] == null)
						throw new CsoGatewayError(ErrorMessage.L_000, info.typeName, typeInfo.jsNamespace, typeInfo.typeName);

					dependencyType = allCsoTypes[String.Format("{0}.{1}", typeInfo.jsNamespace, typeInfo.typeName)];

					nativeType.addStrongDependency(dependencyType);
				}
			}
		}

		private static void createWeakDependencies(TypeInformation info, CsoNativeType nativeType)
		{
			TypeInformation typeInfo;
			CsoType dependencyType;

			if (info.WeakDependency != null)
			{
				foreach (DependencyInformation dependencyInfo in info.WeakDependency)
				{
					typeInfo = csoNativeTypesLibrary.getCsoNativeTypeInformation(dependencyInfo.id);

					if (allCsoTypes[String.Format("{0}.{1}", typeInfo.jsNamespace, typeInfo.typeName)] == null)
						throw new CsoGatewayError(ErrorMessage.L_000, info.typeName, typeInfo.jsNamespace, typeInfo.typeName);

					dependencyType = allCsoTypes[String.Format("{0}.{1}", typeInfo.jsNamespace, typeInfo.typeName)];

					nativeType.addWeakDependency(dependencyType);
				}
			}
		}

		#endregion

		#region Creation of CsoGeneratedTypes
		internal static void createCsoGeneratedType(Type aType)
		{
			if (!mapsToCompletedCsoType(aType))
			{
				/*
				 * This mutual-exclusion lock is necessary because two threads could end up creating the same cso-generated type.
				 * Now only one thread can be busy creating cso-generated types at any time.
				 */
				lock (allCsoTypes.SyncRoot)
				{
					if (!mapsToCompletedCsoType(aType))
					{
						assertCsoReady(aType);

						CsoGeneratedType generatedType;
						generatedType = new CsoGeneratedType();
						generatedType.ServerSideType = aType;
						generatedType.NamespaceName = getCsoGeneratedTypeNamespaceName(aType);
						generatedType.TypeName = getCsoGeneratedTypeName(aType);
						generatedType.LookupBaseType = !ignoreBaseTypeForType(aType);
						generatedType.ExtensionUrl = getExtensionUrlForType(aType);

						allCsoTypes.addCsoGeneratedType(generatedType);

						if (aType.IsClass)
						{
							if (generatedType.LookupBaseType)
							{
								Type aBaseType = aType.BaseType;

								while(aBaseType != null)
								{
									if(isCsoReady(aBaseType))
									{
										createCsoGeneratedType(aBaseType);
										break;
									}
									else
									{
										aBaseType = aBaseType.BaseType;
									}
								}
							}

							Type[] allInterfaces = aType.GetInterfaces();
							for (int i = 0; i < allInterfaces.Length; ++i)
							{
								if (isCsoReady(allInterfaces[i]))
								{
									createCsoGeneratedType(allInterfaces[i]);
								}
							}

							FieldInfo[] staticFieldInfos = aType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							for (int i = 0; i < staticFieldInfos.Length; ++i)
							{
								if (staticFieldInfos[i].IsLiteral)
								{
									if (isCsoReady(staticFieldInfos[i].FieldType))
									{
										createCsoGeneratedType(staticFieldInfos[i].FieldType);
									}
									else if (isNullableOfCsoReadyType(staticFieldInfos[i].FieldType))
									{
										createCsoGeneratedType(staticFieldInfos[i].FieldType.GetGenericArguments()[0]);
									}
								}
							}

							MethodInfo propGetAccessor;
							Type propReturnType;

							foreach (PropertyInfo prop in aType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
							{
								if (!isPropertyExcluded(prop))
								{
									propGetAccessor = prop.GetGetMethod(false);

									if (propGetAccessor != null)
									{
										// Generic classes and structs are never cso-ready so there is no need to normalize the type
										propReturnType = propGetAccessor.ReturnParameter.ParameterType;
										if (isCsoReady(propReturnType))
										{
											createCsoGeneratedType(propReturnType);
										}
										else if (isNullableOfCsoReadyType(propReturnType))
										{
											createCsoGeneratedType(propReturnType.GetGenericArguments()[0]);
										}
									}
								}
							}

							createCsoGeneratedClassSourceCode(generatedType);
							resolveCsoGeneratedTypeDependencies((CsoGeneratedType)generatedType);
							generatedType.markAsComplete();
						}
						else if (aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive) // Is a non-primitive type struct
						{
							Type[] allInterfaces = aType.GetInterfaces();
							for (int i = 0; i < allInterfaces.Length; ++i)
							{
								if (isCsoReady(allInterfaces[i]))
								{
									createCsoGeneratedType(allInterfaces[i]);
								}
							}

							MethodInfo propGetAccessor;
							Type propReturnType;

							foreach (PropertyInfo prop in aType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
							{
								if (!isPropertyExcluded(prop))
								{
									propGetAccessor = prop.GetGetMethod(false);

									if (propGetAccessor != null)
									{
										propReturnType = propGetAccessor.ReturnParameter.ParameterType;
										if (isCsoReady(propReturnType))
										{
											createCsoGeneratedType(propReturnType);
										}
										else if (isNullableOfCsoReadyType(propReturnType))
										{
											createCsoGeneratedType(propReturnType.GetGenericArguments()[0]);
										}
									}
								}
							}

							createCsoGeneratedStructSourceCode(generatedType);
							resolveCsoGeneratedTypeDependencies((CsoGeneratedType)generatedType);
							generatedType.markAsComplete();
						}
						else if (aType.IsInterface)
						{
							createCsoGeneratedInterfaceSourceCode(generatedType);
							generatedType.markAsComplete();
						}
						else if (aType.IsEnum)
						{
							createCsoGeneratedEnumSourceCode(generatedType);
							generatedType.markAsComplete();
						}
					}
				}
			}
		}

		private static string getCsoGeneratedTypeNamespaceName(Type aType)
		{
			string name = null;

			object[] csoGenAttrs = aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false);
			CsoGeneratedTypeAttribute csoGenAttr = null;
			if (csoGenAttrs.Length > 0)
				csoGenAttr = (CsoGeneratedTypeAttribute)aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)[0];

			if (csoGenAttr != null && !String.IsNullOrEmpty(csoGenAttr.ClientSideNamespace))
			{
				name = csoGenAttr.ClientSideNamespace;
			}
			else if (ConfigurationProxy.Instance.CsoGatewayConfig.ToClientSideConfig.ClientSideNamespaceOfTypes.ContainsElementForType(aType))
			{
				name = ConfigurationProxy.Instance.CsoGatewayConfig.ToClientSideConfig.ClientSideNamespaceOfTypes.ValueForType(aType);
			}
			else
			{
				name = ConfigurationProxy.Instance.CsoGatewayConfig.ToClientSideConfig.DefaultNamespace;
			}

			return name;
		}

		private static string getCsoGeneratedTypeName(Type aType)
		{
			return aType.Name;
		}

		private static bool ignoreBaseTypeForType(Type aType)
		{
			bool ignore = false;

			if (aType.IsClass)
			{
				object[] csoGenAttrs = aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false);

				if (csoGenAttrs.Length > 0)
				{
					CsoGeneratedTypeAttribute csoGenAttr = csoGenAttrs[0] as CsoGeneratedTypeAttribute;

					ignore = (csoGenAttr.IgnoreBaseClass != null ? csoGenAttr.IgnoreBaseClass.Value :
						ConfigurationProxy.Instance.CsoGatewayConfig.ToClientSideConfig.IgnoreBaseClass);
				}
				else
				{
					ignore = false;
				}
			}
			else
			{
				ignore = true;
			}

			return ignore;
		}

		private static Uri getExtensionUrlForType(Type aType)
		{
			Uri url = null;

			if (aType.IsClass || (aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive))
			{
				CsoGeneratedTypeAttribute csoGenAttr = (CsoGeneratedTypeAttribute)aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)[0];

				if (!String.IsNullOrEmpty(csoGenAttr.ExtensionUrl))
				{
					url = new Uri(csoGenAttr.ExtensionUrl, UriKind.RelativeOrAbsolute);
				}
				else if (ConfigurationProxy.Instance.CsoGatewayConfig.ToClientSideConfig.ExtensionUrls.ContainsElementForType(aType))
				{
					url = new Uri(ConfigurationProxy.Instance.CsoGatewayConfig.ToClientSideConfig.ExtensionUrls.ValueForType(aType), UriKind.RelativeOrAbsolute);
				}
			}

			return url;
		}

		#endregion

		#region Creation of type-defining-script for classes and structs

		private static void createCsoGeneratedClassSourceCode(CsoGeneratedType csoGenType)
		{
			StringBuilder sb = new StringBuilder();
			CsoGeneratedTypeAttribute csoGenAttr = (CsoGeneratedTypeAttribute)csoGenType.ServerSideType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)[0];

			// Const field
			List<FieldInfo> allConstFields;
			allConstFields = getConstFields(csoGenType.ServerSideType);

			// Public properties
			List<PropertyInfo> allDefinedProperties;
			List<PropertyInfo> declaredProperties;
			List<PropertyInfo> propertiesDeclaredInBaseClasses;

			if (csoGenType.LookupBaseType)
			{
				allDefinedProperties = getClassProperties(csoGenType.ServerSideType, csoGenType.ServerSideType, true);
				declaredProperties = getClassProperties(csoGenType.ServerSideType, csoGenType.ServerSideType, false);
				propertiesDeclaredInBaseClasses = allDefinedProperties.GetRange(declaredProperties.Count, allDefinedProperties.Count - declaredProperties.Count);
			}
			else
			{
				allDefinedProperties = getClassProperties(csoGenType.ServerSideType, csoGenType.ServerSideType, false);
				declaredProperties = allDefinedProperties;
				propertiesDeclaredInBaseClasses = new List<PropertyInfo>();
			}

			List<PropertyInfo> hashSources = getHashSources(allDefinedProperties);

			string clientSideNamespace = getCsoGeneratedTypeNamespaceName(csoGenType.ServerSideType);
			string constructorParamsString = createParamString(allDefinedProperties);
			string constructorBaseCallParamString = createParamString(propertiesDeclaredInBaseClasses);

			// Comments
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// CSO Gateway version {0}", csoGatewayAssembly.GetName().Version.ToString()));
			sb.AppendLine("// Copyright (C) 2010 Pascal Dufresne");
			sb.AppendLine("// http://csogateway.codeplex.com");
			sb.AppendLine("// http://csogateway.metaobjects.ca");
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// class {0}.{1}", clientSideNamespace, csoGenType.ServerSideType.Name));
			sb.AppendLine(String.Format("// dynamically generated from class: \"{0}\"", csoGenType.ServerSideType.AssemblyQualifiedName));
			sb.AppendLine("// ****************************************");
			sb.AppendLine();
			sb.AppendLine();

			// Namespace of class
			sb.AppendLine("Type.registerNamespace('" + clientSideNamespace + "');");
			sb.AppendLine();

			// Constructor
			sb.AppendLine(String.Format("{0}.{1} = function {0}${1}({2})", clientSideNamespace, csoGenType.ServerSideType.Name, constructorParamsString));
			sb.AppendLine("{");

			// Constructor - Initialize base class
			if (propertiesDeclaredInBaseClasses.Count > 0)
			{
				sb.AppendLine(String.Format("  {0}.{1}.initializeBase(this, [{2}]);", clientSideNamespace, csoGenType.ServerSideType.Name, constructorBaseCallParamString));
				sb.AppendLine();
			}

			// Constructor - Assign params to fields
			foreach (PropertyInfo prop in declaredProperties)
			{
				sb.AppendLine(String.Format("  this._{0} = {0};", prop.Name));
			}
			sb.AppendLine("}");
			sb.AppendLine();

			// Const fields
			foreach (FieldInfo info in allConstFields)
			{
				sb.Append(String.Format("{0}.{1}.{2} = ", clientSideNamespace, csoGenType.ServerSideType.Name, info.Name));
				ToClientSideMainConverter.RenderCsoCompatibleObject(info.GetValue(null), sb);
				sb.AppendLine(String.Format(";"));
			}
			if (allConstFields.Count > 0)
				sb.AppendLine();

			// Properties
			foreach (PropertyInfo prop in declaredProperties)
			{
				sb.AppendLine("// " + prop.Name + " property");
				sb.AppendLine(String.Format("{0}.{1}.prototype.get_{2} = function {0}${1}$get_{2}()", clientSideNamespace, csoGenType.ServerSideType.Name, prop.Name));
				sb.AppendLine("{");
				sb.AppendLine("  return this._" + prop.Name + ";");
				sb.AppendLine("}");
				sb.AppendLine(String.Format("{0}.{1}.prototype.set_{2} = function {0}${1}$set_{2}(value)", clientSideNamespace, csoGenType.ServerSideType.Name, prop.Name));
				sb.AppendLine("{");
				sb.AppendLine("  this._" + prop.Name + " = value;");
				sb.AppendLine("}");
				sb.AppendLine();
			}

			// Hash sources
			if (hashSources.Count > 1)
			{
				sb.AppendLine("// Hash sources");
				sb.AppendLine(String.Format("{0}.{1}.prototype.getCsoHashSource = function {0}${1}$getCsoHashSource()", clientSideNamespace, csoGenType.ServerSideType.Name));
				sb.AppendLine("{");
				sb.AppendLine("  return new Array(");
				for (int i = 0; i < hashSources.Count; i++)
				{
					sb.AppendLine("    this._" + hashSources[i].Name + ((i == (hashSources.Count - 1)) ? String.Empty : ","));
				}
				sb.AppendLine("  );");
				sb.AppendLine("}");
			}
			else if (hashSources.Count == 1) // Count == 1 handled as a special case because in ctor new Array(Number) the argument is not an element
			{
				sb.AppendLine("// Hash sources");
				sb.AppendLine(String.Format("{0}.{1}.prototype.getCsoHashSource = function {0}${1}$getCsoHashSource()", clientSideNamespace, csoGenType.ServerSideType.Name));
				sb.AppendLine("{");
				sb.AppendLine("  var x = new Array();");
				sb.AppendLine("  x[0]= this._" + hashSources[0].Name + ";");
				sb.AppendLine("  return x;");
				sb.AppendLine("}");
			}

			// Register class                                       
			sb.AppendLine(String.Format(
				"{0}.{1}.registerClass('{0}.{1}', {2}{3}{4});",
				clientSideNamespace,
				csoGenType.ServerSideType.Name,
				getCsoGeneratedTypeBaseClassFullName(csoGenType.ServerSideType, csoGenType.LookupBaseType),
				getFunctionalInterfaces(true, hashSources.Count > 0),
				getImplementedCsoInterfaces(csoGenType)
				));


			csoGenType.DefinedProperties = allDefinedProperties;
			csoGenType.SourceCode = sb.ToString();
		}

		private static void createCsoGeneratedStructSourceCode(CsoGeneratedType csoGenType)
		{
			StringBuilder sb = new StringBuilder();
			CsoGeneratedTypeAttribute csoGenAttr = (CsoGeneratedTypeAttribute)csoGenType.ServerSideType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)[0];

			List<FieldInfo> allConstFields = getConstFields(csoGenType.ServerSideType);
			List<PropertyInfo> allDefinedProperties = getStructProperties(csoGenType.ServerSideType, csoGenType.ServerSideType);
			List<PropertyInfo> hashSources = getHashSources(allDefinedProperties);

			string clientSideNamespace = getCsoGeneratedTypeNamespaceName(csoGenType.ServerSideType);
			string constructorParamsString = createParamString(allDefinedProperties);

			// Comments
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// CSO Gateway version {0}", csoGatewayAssembly.GetName().Version.ToString()));
			sb.AppendLine("// Copyright (C) 2010 Pascal Dufresne");
			sb.AppendLine("// http://csogateway.codeplex.com");
			sb.AppendLine("// http://csogateway.metaobjects.ca");
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// class {0}.{1}", clientSideNamespace, csoGenType.ServerSideType.Name));
			sb.AppendLine(String.Format("// dynamically generated from struct: \"{0}\"", csoGenType.ServerSideType.AssemblyQualifiedName));
			sb.AppendLine("// ****************************************");
			sb.AppendLine();
			sb.AppendLine();

			// Namespace of struct
			sb.AppendLine("  Type.registerNamespace('" + clientSideNamespace + "');");
			sb.AppendLine();

			// Constructor
			sb.AppendLine(String.Format("{0}.{1} = function {0}${1}({2})", clientSideNamespace, csoGenType.ServerSideType.Name, constructorParamsString));
			sb.AppendLine("{");

			// Constructor - Assign params to fields
			foreach (PropertyInfo prop in allDefinedProperties)
			{
				sb.AppendLine(String.Format("  this._{0} = {0};", prop.Name));
			}
			sb.AppendLine("}");
			sb.AppendLine();

			// Const fields
			foreach (FieldInfo info in allConstFields)
			{
				sb.Append(String.Format("{0}.{1}.{2} = ", clientSideNamespace, csoGenType.ServerSideType.Name, info.Name));
				ToClientSideMainConverter.RenderCsoCompatibleObject(info.GetValue(null), sb);
				sb.AppendLine(String.Format(";"));
			}
			if (allConstFields.Count > 0)
				sb.AppendLine();

			// Properties
			foreach (PropertyInfo prop in allDefinedProperties)
			{
				sb.AppendLine("// " + prop.Name + " property");
				sb.AppendLine(String.Format("{0}.{1}.prototype.get_{2} = function {0}${1}$get_{2}()", clientSideNamespace, csoGenType.ServerSideType.Name, prop.Name));
				sb.AppendLine("{");
				sb.AppendLine("  return this._" + prop.Name + ";");
				sb.AppendLine("}");
				sb.AppendLine(String.Format("{0}.{1}.prototype.set_{2} = function {0}${1}$set_{2}(value)", clientSideNamespace, csoGenType.ServerSideType.Name, prop.Name));
				sb.AppendLine("{");
				sb.AppendLine("  this._" + prop.Name + " = value;");
				sb.AppendLine("}");
				sb.AppendLine();
			}

			// Hash sources
			if (hashSources.Count > 1)
			{
				sb.AppendLine("// Hash sources");
				sb.AppendLine(String.Format("{0}.{1}.prototype.getCsoHashSource = function {0}${1}$getCsoHashSource()", clientSideNamespace, csoGenType.ServerSideType.Name));
				sb.AppendLine("{");
				sb.AppendLine("  return new Array(");
				for (int i = 0; i < hashSources.Count; i++)
				{
					sb.AppendLine("    this._" + hashSources[i].Name + ((i == (hashSources.Count - 1)) ? String.Empty : ","));
				}
				sb.AppendLine("  );");
				sb.AppendLine("}");
			}
			else if (hashSources.Count == 1) // Count == 1 handled as a special case because in ctor new Array(Number) the argument is not an element
			{
				sb.AppendLine("// Hash sources");
				sb.AppendLine(String.Format("{0}.{1}.prototype.getCsoHashSource = function {0}${1}$getCsoHashSource()", clientSideNamespace, csoGenType.ServerSideType.Name));
				sb.AppendLine("{");
				sb.AppendLine("  var x = new Array();");
				sb.AppendLine("  x[0]= this._" + hashSources[0].Name + ";");
				sb.AppendLine("  return x;");
				sb.AppendLine("}");
			}

			// Register class                                       
			sb.AppendLine(String.Format(
				"{0}.{1}.registerClass('{0}.{1}', {2}{3}{4});",
				clientSideNamespace,
				csoGenType.ServerSideType.Name,
				getCsoGeneratedTypeBaseClassFullName(csoGenType.ServerSideType, false),
				getFunctionalInterfaces(true, hashSources.Count > 0),
				getImplementedCsoInterfaces(csoGenType)
				));


			csoGenType.DefinedProperties = allDefinedProperties;
			csoGenType.SourceCode = sb.ToString();
		}

		private static List<FieldInfo> getConstFields(Type aType)
		{
			List<FieldInfo> fields = new List<FieldInfo>();

			if (aType != null)
			{
				FieldInfo[] fieldInfos = aType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				for (int i = 0; i < fieldInfos.Length; ++i)
				{
					if (fieldInfos[i].IsLiteral && isCsoCompatible(fieldInfos[i].FieldType))
						fields.Add(fieldInfos[i]);
				}
			}

			return fields;
		}

		private static List<PropertyInfo> getClassProperties(Type aType, Type targetType, bool includeBaseClasses)
		{
			List<PropertyInfo> properties = new List<PropertyInfo>();

			if (aType != null)
			{
				foreach (PropertyInfo prop in aType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
				{
					if (!isPropertyExcluded(prop))
					{
						MethodInfo propMethodInfo = prop.GetGetMethod(false);

						//Property is considered only if it has a get accessor
						if (propMethodInfo != null)
						{
							//Make sure the property is cso-compatible
							if (!isPropertyCsoCompatible(propMethodInfo)) // (!isPropertyCsoCompatible(propMethodInfo) && !isNullableOfCsoCompatibleProperty(propMethodInfo))
							{
								throw new CsoGatewayException(ExceptionMessage.TM_002, aType.FullName, propMethodInfo.Name, propMethodInfo.ReturnParameter.ParameterType.FullName);
							}

							//Verify if property is also declared in a parent class. No need to verify if property is excluded here. 
							if (isPropertyTopMost(prop))
							{
								if (propMethodInfo.IsVirtual)
								{
									// If the property is virtual, we going to get the correct value no matter which PropertyInfo object along the inheritance
									// chain we call invoke on, the top one with the abstract/virtual or one below with the overrides modifer. But it is still
									// important for uniformity to search for the lowest one in the inheritance chain that is above the type
									// specified by the parameter 'targetType'. But here we don't care about the found property being decorated with the
									// CsoPropertyExclude attribute because the property is virtual and the same value is returned by all these PropertyInfo
									// objects. Decorating an 'overrides' property with the CsoPropertyExclude attribute has no effect.

									bool found = false;
									Type currentType = targetType;
									PropertyInfo foundProperty = null;

									while ((currentType != null) && (currentType != aType))
									{
										foundProperty = currentType.GetProperty(prop.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

										if ((foundProperty != null) && (foundProperty.GetGetMethod(false) != null))
										{
											found = true;
											break;
										}

										currentType = currentType.BaseType;
									}

									if (found)
										properties.Add(foundProperty);
									else
										properties.Add(prop);
								}
								else
								{
									// If the property is not virtual, it might be hidden in a subclass one or more times. If it is, the PropertyInfo
									// that we must add to the list of the properties is the lowest one in the inheritance chain that is above the type
									// specified by the parameter 'targetType'

									bool found = false;
									Type currentType = targetType;
									PropertyInfo foundProperty = null;

									while ((currentType != null) && (currentType != aType))
									{
										foundProperty = currentType.GetProperty(prop.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

										if ((foundProperty != null) && (foundProperty.GetGetMethod(false) != null) && !isPropertyExcluded(foundProperty))
										{
											found = true;
											break;
										}

										currentType = currentType.BaseType;
									}

									if (found)
										properties.Add(foundProperty);
									else
										properties.Add(prop);

								}
							}
						}
					}
				}

				if (!ignoreBaseTypeForType(aType))
				{
					// If 'includeBaseClasses' is true, the recursion continue until the end of the inheritance chain.
					// Else the recusion continues only while the base class does NOT map to a CSO generated class.
					if (includeBaseClasses && !ignoreBaseTypeForType(aType))
					{
						properties.AddRange(getClassProperties(aType.BaseType, targetType, true));
					}
					else if (aType.BaseType != null && !mapsToCsoGeneratedType(aType.BaseType))
					{
						properties.AddRange(getClassProperties(aType.BaseType, targetType, false));
					}
				}
			}

			return properties;
		}

		private static List<PropertyInfo> getStructProperties(Type aType, Type targetType)
		{
			List<PropertyInfo> properties = new List<PropertyInfo>();

			if (aType != null)
			{
				foreach (PropertyInfo prop in aType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
				{
					if (System.Attribute.GetCustomAttributes(prop, typeof(CsoPropertyExcludeAttribute), false).Length == 0)
					{
						MethodInfo propMethodInfo = prop.GetGetMethod(false);

						//Property is considered only if it has a get accessor
						if (propMethodInfo != null)
						{
							//Make sure the property is cso-compatible
							if (!isPropertyCsoCompatible(propMethodInfo)) // (!isPropertyCsoCompatible(propMethodInfo) && !isNullableOfCsoCompatibleProperty(propMethodInfo))
							{
								throw new CsoGatewayException(ExceptionMessage.TM_002, aType.FullName, propMethodInfo.Name, propMethodInfo.ReturnParameter.ParameterType.FullName);
							}

							properties.Add(prop);
						}
					}
				}
			}

			return properties;
		}

		private static List<PropertyInfo> getHashSources(List<PropertyInfo> definedProperties)
		{
			List<PropertyInfo> hashSourceProperties = new List<PropertyInfo>();

			foreach (PropertyInfo prop in definedProperties)
			{
				if (System.Attribute.GetCustomAttributes(prop, typeof(CsoHashSourceAttribute), true).Length > 0)
				{
					hashSourceProperties.Add(prop);
				}
			}

			return hashSourceProperties;
		}

		private static bool isPropertyExcluded(PropertyInfo propInfo)
		{
			bool excluded = false;

			Type currentType = propInfo.DeclaringType;
			PropertyInfo propInfoInCurrentType;

			while (currentType != null)
			{
				propInfoInCurrentType = currentType.GetProperty(propInfo.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);

				if (propInfoInCurrentType != null && propInfoInCurrentType.GetGetMethod() != null &&
					(System.Attribute.GetCustomAttributes(propInfoInCurrentType, typeof(CsoPropertyExcludeAttribute), false).Length > 0))
				{
					excluded = true;
					break;
				}

				// Do not continue to look up the inheritance chain if base classes of the current type are to be ignored
				if (currentType.IsClass && !ignoreBaseTypeForType(currentType))
				{
					currentType = currentType.BaseType;
				}
				else
				{
					currentType = null;
				}
			}

			return excluded;
		}

		private static bool isPropertyTopMost(PropertyInfo propInfo)
		{
			bool isTopMost = true;
			Type currentType = propInfo.DeclaringType;
			PropertyInfo aProperty = null;

			while (currentType.BaseType != null)
			{
				if (mapsToCsoGeneratedType(currentType) && !((CsoGeneratedType)allCsoTypes[currentType]).LookupBaseType)
				{
					// A type decorated with CsoGeneratedType and the flag IgnoreBaseClass set to true has been encountered. Break out of the
					// loop here because it no longer matters if there is an excluded ancestor property.
					break;
				}
				else
				{
					currentType = currentType.BaseType;
				}

				aProperty = currentType.GetProperty(propInfo.Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);

				if (aProperty != null && !isPropertyExcluded(aProperty))
				{
					// Found an ancestor property that is not exclude, propInfo is not the top most
					isTopMost = false;
					break;
				}

			}

			return isTopMost;

		}

		private static bool isPropertyCsoCompatible(MethodInfo info)
		{
			return isCsoCompatible(info.ReturnParameter.ParameterType);
		}

		/*
		private static bool isNullableOfCsoCompatibleProperty(MethodInfo info)
		{
			if (info.ReturnParameter.ParameterType.IsGenericType &&
				info.ReturnParameter.ParameterType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
			{
				return isCsoCompatible(info.ReturnParameter.ParameterType.GetGenericArguments()[0]);
			}
			else
			{
				return false;
			}
		}
		*/

		private static string createParamString(List<PropertyInfo> properties)
		{
			string[] parameters = new string[properties.Count];

			for (int i = 0; i < properties.Count; ++i)
			{
				parameters[i] = properties[i].Name;
			}

			return String.Join(", ", parameters);
		}

		private static string getCsoGeneratedTypeBaseClassFullName(Type type, bool lookupServerSideBaseType)
		{
			string parentTypeName = String.Empty;

			if (type.IsClass || (type.IsValueType && !type.IsEnum && !type.IsPrimitive))
			{
				if (lookupServerSideBaseType)
				{
					Type aBaseType = type.BaseType;
					object[] csoGenAttrs;

					while (aBaseType != null)
					{
						csoGenAttrs = aBaseType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false);

						if (csoGenAttrs.Length > 0)
						{
							CsoGeneratedTypeAttribute csoGenAttr = csoGenAttrs[0] as CsoGeneratedTypeAttribute;
							parentTypeName = getCsoGeneratedTypeNamespaceName(aBaseType) + "." + aBaseType.Name;
							break;
						}
						else
						{
							aBaseType = aBaseType.BaseType;
						}
					}
				}

				if (String.IsNullOrEmpty(parentTypeName))
				{
					parentTypeName = "CsoGateway.System.CsoGenerated";
				}

			}
			else
			{
				throw new CsoGatewayError(ErrorMessage.TM_000, type.FullName);
			}

			return parentTypeName;
		}

		private static string getFunctionalInterfaces(bool implementsDisposable, bool implementsHashProvider)
		{
			string interfaces = String.Empty;

			if (implementsDisposable)
			{
				interfaces += (", Sys.IDisposable");
			}
			if (implementsHashProvider)
			{
				interfaces += (", CsoGateway.Collections.ICsoHashProvider");
			}

			return interfaces;
		}

		private static string getImplementedCsoInterfaces(CsoGeneratedType csoGenType)
		{
			string csoInterfaces = String.Empty;
			Type aType = csoGenType.ServerSideType;

			if ((aType.IsClass) ||
				(aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive))
			{
				Type[] allInterfaces = csoGenType.ServerSideType.GetInterfaces();
				for (int i = 0; i < allInterfaces.Length; ++i)
				{
					if (mapsToCompletedCsoGeneratedType(allInterfaces[i]))
					{
						csoInterfaces += (", " + allCsoTypes[allInterfaces[i]].FullTypeName);
					}
				}
			}

			return csoInterfaces;
		}

		private static void resolveCsoGeneratedTypeDependencies(CsoGeneratedType csoGeneratedType)
		{
			Type type = csoGeneratedType.ServerSideType;

			// Find first parent that is cso-compatible. Add the dependency. If there is none, CsoGateway.System.CsoGenerated is the dependency.
			if (csoGeneratedType.LookupBaseType)
			{
				Type aBaseType = type.BaseType;
				bool ancestorFound = false;

				while (aBaseType != null)
				{
					if (mapsToCsoType(aBaseType))
					{
						csoGeneratedType.addStrongDependency(allCsoTypes[aBaseType]);
						ancestorFound = true;
						break;
					}
					else
					{
						aBaseType = aBaseType.BaseType;
					}
				}

				if(!ancestorFound)
					csoGeneratedType.addStrongDependency(allCsoTypes["CsoGateway.System.CsoGenerated"]);
			}
			else
			{
				csoGeneratedType.addStrongDependency(allCsoTypes["CsoGateway.System.CsoGenerated"]);
			}



			// Add a strong dependency to all cso-generated interfaces that correspond to a cso-compatible interface implemented by the server-side type.
			Type[] allInterfaces = csoGeneratedType.ServerSideType.GetInterfaces();
			for (int i = 0; i < allInterfaces.Length; ++i)
			{
				if (mapsToCsoGeneratedType(allInterfaces[i]))
				{
					csoGeneratedType.addStrongDependency(allCsoTypes[allInterfaces[i]]);
				}
			}

			// Add a strong dependency to the cso-type corresponding to the type of static, 'literal' fields. (Literal means const in C#.
			FieldInfo[] staticFieldInfos = csoGeneratedType.ServerSideType.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			
			for (int i = 0; i < staticFieldInfos.Length; ++i)
			{
				if (staticFieldInfos[i].IsLiteral)
				{
					if (mapsToCsoType(staticFieldInfos[i].FieldType))
					{
						csoGeneratedType.addStrongDependency(allCsoTypes[staticFieldInfos[i].FieldType]);
					}
				}
			}

			// Look at the return type of each properties and add a weak dependencies
			MethodInfo propGetAccessor;
			Type propReturnType;
			foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
			{
				if (!isPropertyExcluded(prop))
				{
					propGetAccessor = prop.GetGetMethod(false);
					if (propGetAccessor != null)
					{
						propReturnType = propGetAccessor.ReturnParameter.ParameterType;

						if (mapsToCsoType(propReturnType))
						{
							csoGeneratedType.addWeakDependency(allCsoTypes[propReturnType]);
						}

						if (prop.GetCustomAttributes(typeof(CsoHashSourceAttribute), false).Length > 0)
						{
							csoGeneratedType.addStrongDependency(allCsoTypes["CsoGateway.Collections.ICsoHashProvider"]);
							csoGeneratedType.addStrongDependency(allCsoTypes["CsoGateway.Collections.Hashing"]);
						}
					}
				}
			}
		}

		#endregion

		#region Creation of type-defining-script for interfaces
		public static void createCsoGeneratedInterfaceSourceCode(CsoGeneratedType csoGenType)
		{
			StringBuilder sb = new StringBuilder();
			CsoGeneratedTypeAttribute csoGenAttr = (CsoGeneratedTypeAttribute)csoGenType.ServerSideType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)[0];

			string clientSideNamespace = getCsoGeneratedTypeNamespaceName(csoGenType.ServerSideType);

			// Comments
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// CSO Gateway version {0}", csoGatewayAssembly.GetName().Version.ToString()));
			sb.AppendLine("// Copyright (C) 2010 Pascal Dufresne");
			sb.AppendLine("// http://csogateway.codeplex.com");
			sb.AppendLine("// http://csogateway.metaobjects.ca");
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// interface {0}.{1}", clientSideNamespace, csoGenType.ServerSideType.Name));
			sb.AppendLine(String.Format("// dynamically generated from interface: \"{0}\"", csoGenType.ServerSideType.AssemblyQualifiedName));
			sb.AppendLine("// ****************************************");
			sb.AppendLine();
			sb.AppendLine();

			// Namespace of interface
			sb.AppendLine("Type.registerNamespace('" + clientSideNamespace + "');");
			sb.AppendLine();

			// Constructor
			sb.AppendLine(String.Format("{0}.{1} = function {0}${1}()", clientSideNamespace, csoGenType.ServerSideType.Name));
			sb.AppendLine("{");
			sb.AppendLine("}");
			sb.AppendLine();

			// Register interface                                       
			sb.AppendLine(String.Format(
				"{0}.{1}.registerInterface('{0}.{1}');",
				clientSideNamespace,
				csoGenType.ServerSideType.Name
				));

			csoGenType.SourceCode = sb.ToString();
			
		}
		#endregion

		#region Creation of type-defining-script for enums

		private static void createCsoGeneratedEnumSourceCode(CsoGeneratedType csoGenType)
		{
			StringBuilder sb = new StringBuilder();

			string clientSideNamespace = getCsoGeneratedTypeNamespaceName(csoGenType.ServerSideType);

			// Comments
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// CSO Gateway version {0}", csoGatewayAssembly.GetName().Version.ToString()));
			sb.AppendLine("// Copyright (C) 2010 Pascal Dufresne");
			sb.AppendLine("// http://csogateway.codeplex.com");
			sb.AppendLine("// http://csogateway.metaobjects.ca");
			sb.AppendLine("// ****************************************");
			sb.AppendLine(String.Format("// enum {0}.{1}", clientSideNamespace, csoGenType.ServerSideType.Name));
			sb.AppendLine(String.Format("// dynamically generated from enum: \"{0}\"", csoGenType.ServerSideType.AssemblyQualifiedName));
			sb.AppendLine("// ****************************************");
			sb.AppendLine();
			sb.AppendLine();

			sb.AppendLine("Type.registerNamespace('" + csoGenType.NamespaceName + "');");
			sb.AppendLine();

			sb.AppendLine(String.Format("{0}.{1} = function(){{}};", csoGenType.NamespaceName, csoGenType.ServerSideType.Name));
			sb.AppendLine(String.Format("{0}.{1}.prototype = ", csoGenType.NamespaceName, csoGenType.ServerSideType.Name));
			sb.AppendLine("{");
			sb.AppendLine(getEnumElements(csoGenType.ServerSideType));
			sb.AppendLine("}");
			sb.AppendLine();

			sb.AppendLine(String.Format("{0}.{1}.registerEnum('{0}.{1}');", csoGenType.NamespaceName, csoGenType.ServerSideType.Name));

			csoGenType.SourceCode = sb.ToString();
		}

		private static string getEnumElements(Type type)
		{
			StringBuilder sb = new StringBuilder();
			List<string> names = new List<string>();
			List<string> values = new List<string>();

			foreach (string name in Enum.GetNames(type))
			{
				names.Add(name);
			}
			foreach (object integerValue in Enum.GetValues(type))
			{
				values.Add((((System.Enum)integerValue).ToString("D")));
			}
			for (int i = 0; i < names.Count; i++)
			{
				sb.Append(String.Format("  {0}:{1}", names[i], values[i]));
				if (i < names.Count - 1)
				{
					sb.AppendLine(",");
				}
			}

			return sb.ToString();
		}


		#endregion

		#region Cso-ready

		internal static bool isCsoReady(Type aType)
		{
			return (
				(aType.IsEnum && !mapsToCsoType(aType)) ||
				(!aType.IsGenericType && !aType.IsNested && !mapsToCsoType(aType) && aType.IsClass && (aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)).Length > 0) ||
				(!aType.IsGenericType && !aType.IsNested && !mapsToCsoType(aType) && aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive && (aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)).Length > 0) ||
				(!aType.IsGenericType && !aType.IsNested && !mapsToCsoType(aType) && aType.IsInterface && (aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)).Length > 0)
				);
		}

		internal static bool isNullableOfCsoReadyType(Type aType)
		{
			if ((aType.IsGenericType) && (aType.GetGenericTypeDefinition() == typeof(Nullable<>)))
			{
				return isCsoReady(aType.GetGenericArguments()[0]);
			}
			else
			{
				return false;
			}
		}

		internal static void assertCsoReady(Type aType)
		{
			if (!isCsoReady(aType))
			{
				if (mapsToCsoType(aType))
					throw new CsoGatewayError(ErrorMessage.L_001, aType.FullName, getCsoTypeForType(aType).FullTypeName);
				if (!aType.IsEnum && !aType.IsClass && !(aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive))
					throw new CsoGatewayException(ExceptionMessage.L_000, aType.FullName);
				if ((aType.IsClass && aType.IsGenericType) || ((aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive) && aType.IsGenericType))
					throw new CsoGatewayException(ExceptionMessage.L_003 , aType.FullName);
				if (aType.IsClass && ((aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)).Length == 0))
					throw new CsoGatewayError(ErrorMessage.L_002, aType.FullName);
				if (aType.IsValueType && !aType.IsEnum && !aType.IsPrimitive && ((aType.GetCustomAttributes(typeof(CsoGeneratedTypeAttribute), false)).Length == 0))
					throw new CsoGatewayError(ErrorMessage.L_003, aType.FullName);

				throw new CsoGatewayError(ErrorMessage.L_004, aType.FullName);
			}

		}
		#endregion

		#region Utility methods

		private static Stream getResourceStream(string endOfName)
		{
			string fileName = null;
			for (int i = 0; (i < resourceNames.Length) && (fileName == null); ++i)
			{
				if (resourceNames[i].EndsWith(endOfName))
					fileName = resourceNames[i];
			}

			if (fileName == null)
				throw new CsoGatewayError(ErrorMessage.L_005, endOfName);

			return csoGatewayAssembly.GetManifestResourceStream(fileName);
		}

		#endregion

		#region Internal accessors methods
		internal static bool javascriptPrimitiveTypeExistFor(Type aType)
		{
			return mapsToJavascriptPrimitiveType(aType);
		}

		internal static bool javascriptPredefinedCoreObjectExistFor(Type aType)
		{
			return mapsToJavascriptPredifinedCoreObject(aType);
		}

		internal static bool csoTypeExistFor(Type aType)
		{
			return mapsToCompletedCsoType(aType);
		}

		internal static bool csoGeneratedTypeExistFor(Type aType)
		{
			return (mapsToCompletedCsoGeneratedType(aType));
		}

		internal static bool csoNativeTypeExistFor(Type aType)
		{
			return (mapsToCompletedCsoNativeType(aType));
		}

		internal static bool csoTypeExist(string csoTypeName)
		{
			return (allCsoTypes[csoTypeName] != null);
		}

		internal static string getScriptForTypeName(string csoGeneratedTypeName)
		{
			return ((CsoGeneratedType)allCsoTypes[csoGeneratedTypeName]).SourceCode;
		}

		internal static string getScriptForFileName(string fileName)
		{
			return (new StreamReader(getResourceStream(fileName))).ReadToEnd();
		}

		internal static CsoType getCsoTypeForType(Type aType)
		{
			return allCsoTypes[normalizeType(aType)];
		}

		internal static Type getConverterTypeForType(Type aType)
		{
			Type normalizedType = normalizeType(aType);

			return ((CsoNativeType)allCsoTypes[normalizedType]).ServerSideTypesToConverterMapping[normalizedType];
		}
		#endregion

		#region Private accessors methods

		private static bool isCsoCompatible(Type aType)
		{
			return (mapsToJavascriptPrimitiveType(aType) || mapsToCsoType(aType) || mapsToJavascriptPredifinedCoreObject(aType));
		}

		private static bool mapsToJavascriptPrimitiveType(Type aType)
		{
			Type normalizedType = normalizeType(aType);

			return
				(normalizedType == typeof(System.String)) ||
				(normalizedType == typeof(System.Char)) ||
				(normalizedType == typeof(System.Boolean)) ||
				(normalizedType == typeof(System.SByte)) ||
				(normalizedType == typeof(System.Byte)) ||
				(normalizedType == typeof(System.Int16)) ||
				(normalizedType == typeof(System.UInt16)) ||
				(normalizedType == typeof(System.Int32)) ||
				(normalizedType == typeof(System.UInt32)) ||
				(normalizedType == typeof(System.Int64)) ||
				(normalizedType == typeof(System.UInt64)) ||
				(normalizedType == typeof(System.Single)) ||
				(normalizedType == typeof(System.Double)) ||
				(normalizedType == typeof(System.Decimal)) ||
				(normalizedType == typeof(System.Guid)
				);
		}

		private static bool mapsToJavascriptPredifinedCoreObject(Type aType)
		{
			Type normalizedType = normalizeType(aType);

			return
				(normalizedType == typeof(System.DateTime)) ||
				(normalizedType.BaseType == typeof(System.Array)
			);
		}

		private static bool mapsToCsoType(Type aType)
		{
			Type normalizedType = normalizeType(aType);

			return (allCsoTypes[normalizedType] != null);
		}

		private static bool mapsToCsoGeneratedType(Type aType)
		{
			Type normalizedType = normalizeType(aType);
			CsoType csoType = null;

			if (allCsoTypes[normalizedType] != null)
			{
				csoType = allCsoTypes[normalizedType];
			}

			return (csoType != null && csoType is CsoGeneratedType);
		}

		private static bool mapsToCsoNativeType(Type aType)
		{
			Type normalizedType = normalizeType(aType);
			CsoType csoType = null;

			if (allCsoTypes[normalizedType] != null)
			{
				csoType = allCsoTypes[normalizedType];
			}

			return (csoType != null && csoType is CsoNativeType);
		}

		private static Type normalizeType(Type aType)
		{
			if ((aType.IsGenericType) && (aType.GetGenericTypeDefinition() == typeof(Nullable<>)))
			{
				return aType.GetGenericArguments()[0];
			}
			else if (aType.IsGenericType)
			{
				return aType.GetGenericTypeDefinition();
			}
			else
			{
				return aType;
			}
		}

		#endregion

		#region Private accessors methods - CsoTypes with creation completed
		private static bool mapsToCompletedCsoType(Type aType)
		{
			Type normalizedType = normalizeType(aType);
			CsoType csoType = allCsoTypes[normalizedType];

			return (csoType != null && csoType.IsCreationCompleted);
		}

		private static bool mapsToCompletedCsoGeneratedType(Type aType)
		{
			Type normalizedType = normalizeType(aType);
			CsoType csoType = allCsoTypes[normalizedType];

			return (csoType != null && csoType is CsoGeneratedType && csoType.IsCreationCompleted);
		}

		private static bool mapsToCompletedCsoNativeType(Type aType)
		{
			Type normalizedType = normalizeType(aType);
			CsoType csoType = allCsoTypes[normalizedType];

			return (csoType != null && csoType is CsoNativeType && csoType.IsCreationCompleted);
		}

		#endregion

		#region Class CsoTypeCollection
		private class CsoTypeCollection
		{
			private readonly Hashtable csoTypeNameToCsoTypeDictionary;
			private readonly Hashtable typeToCsoTypeDictionary;
			private readonly Hashtable scriptFileNameToScriptFileInfo;

			public CsoTypeCollection()
			{
				csoTypeNameToCsoTypeDictionary = new Hashtable();
				typeToCsoTypeDictionary = new Hashtable();
				scriptFileNameToScriptFileInfo = new Hashtable();
			}

			public CsoType this[string fullTypeName]
			{
				get
				{
					if (csoTypeNameToCsoTypeDictionary.ContainsKey(fullTypeName))
						return (CsoType)csoTypeNameToCsoTypeDictionary[fullTypeName];
					else
						return null;
				}
			}

			public CsoType this[Type aType]
			{
				get
				{
					Type normalizedType = normalizeType(aType);

					if (typeToCsoTypeDictionary.ContainsKey(normalizedType))
					{
						CsoType aCsoType = (CsoType)typeToCsoTypeDictionary[normalizedType];

						// For thread safety. If a CsoType is in the typeToCsoTypeDictionary Hashtable but not in the
						// csoTypeNameToCsoTypeDictionary, the cso-type is being created in another thread. Must return null.
						if (csoTypeNameToCsoTypeDictionary.ContainsKey(aCsoType.FullTypeName))
							return aCsoType;
						else
							return null;
					}
					else
					{
						return null;
					}
				}
			}

			public LibraryScriptFileInformation getScriptFileInfo(string filename)
			{
				if (scriptFileNameToScriptFileInfo.ContainsKey(filename))
				{
					return (LibraryScriptFileInformation)scriptFileNameToScriptFileInfo[filename];
				}
				else
				{
					return null;
				}
			}

			// No need to synchronize this method. Cso-native types are created in the initializer of Loader
			public void addCsoNativeType(CsoNativeType csoType)
			{
				// Add to the type to CsoType relation to the typeToCsoTypeDictionary dictionary
				foreach (Type aType in csoType.ServerSideTypes)
				{
					typeToCsoTypeDictionary[aType] = csoType;
				}

				csoTypeNameToCsoTypeDictionary.Add(csoType.FullTypeName, csoType);
			}

			public void addCsoGeneratedType(CsoGeneratedType csoType)
			{
				csoTypeNameToCsoTypeDictionary.Add(csoType.FullTypeName, csoType);
				typeToCsoTypeDictionary.Add(csoType.ServerSideType, csoType);
			}

			public void addScriptFileInfo(LibraryScriptFileInformation scriptFileInfo)
			{
				scriptFileNameToScriptFileInfo.Add(scriptFileInfo.FileName, scriptFileInfo);
			}

			public void Clear()
			{
				csoTypeNameToCsoTypeDictionary.Clear();
				typeToCsoTypeDictionary.Clear();
				scriptFileNameToScriptFileInfo.Clear();
			}

			public object SyncRoot
			{
				get
				{
					return this;
				}
			}
		}
		#endregion
	}
}
